package com.xiaolei.libraryktx

import android.view.View
import androidx.lifecycle.*
import com.xiaolei.library.Net.RetrofitExt.common.SCallBack
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import retrofit2.Call
import java.lang.Exception

/**
 * Retrofit异步执行的封装
 */
inline fun <reified T> Call<T>.enqueue(
    crossinline onSuccess: ((result: T) -> Unit),
    noinline onFinally: (() -> Unit)? = null,
    noinline onFail: ((t: Throwable?) -> Unit)? = null,
)
{
    this.enqueue(object : SCallBack<T>()
    {
        override fun onSuccess(result: T)
        {
            onSuccess.invoke(result)
        }

        override fun onFinally()
        {
            onFinally?.invoke()
        }

        override fun onFail(call: Call<T>, t: Throwable)
        {
            onFail?.invoke(t)
            onFail ?: let {
                super.onFail(call, t)
            }
        }
    })
}

/**
 * 使用协程的方式来网路请求，会根据生命周期自动取消
 */
inline fun <reified T> Call<T>.enqueue(
    owner: ViewModelStoreOwner,
    noinline onSuccess: ((result: T) -> Unit),
    noinline onFinally: (() -> Unit)? = null,
    noinline onFail: ((t: Throwable?) -> Unit)? = null,
)
{
    val viewModel = ViewModelProvider(owner).get(RetrofitViewModel::class.java)
    viewModel.add(this, onSuccess, onFinally, onFail)
}

class RetrofitViewModel : ViewModel()
{
    fun <T> add(
        call: Call<T>,
        onSuccess: ((result: T) -> Unit),
        onFinally: (() -> Unit)? = null,
        onFail: ((t: Throwable?) -> Unit)? = null,
    )
    {
        val realFail = when
        {
            onFail != null -> onFail
            SCallBack.failEvent != null -> { t ->
                SCallBack.failEvent.onFail(call, t)
            }
            else -> null
        }
        viewModelScope.launch { execute(call, onSuccess, onFinally, realFail) }
    }

    private suspend fun <T> execute(
        call: Call<T>,
        onSuccess: ((result: T) -> Unit)? = null,
        onFinally: (() -> Unit)? = null,
        onFail: ((t: Throwable?) -> Unit)? = null,
    ) = withContext(Dispatchers.Default)
    {
        try
        {
            val response = call.execute()
            if (response.isSuccessful)
            {
                val body = response.body()
                if (body != null)
                {
                    if (onSuccess != null)
                    {
                        viewModelScope.launch(Dispatchers.Main) { onSuccess.invoke(body) }
                    }
                } else
                {
                    if (onFail != null)
                    {
                        viewModelScope.launch(Dispatchers.Main) {
                            onFail.invoke(Exception("response.body() == NULL"))
                        }
                    }

                }
            } else
            {
                if (onFail != null)
                {
                    viewModelScope.launch(Dispatchers.Main) {
                        onFail.invoke(Exception(response.message()))
                    }
                }
            }
        } catch (e: Exception)
        {
            e.printStackTrace()
            if (onFail != null)
            {
                viewModelScope.launch(Dispatchers.Main) {
                    onFail.invoke(e)
                }
            }

        } finally
        {
            if (onFinally != null)
            {
                viewModelScope.launch(Dispatchers.Main) {
                    onFinally.invoke()
                }
            }
        }
        Unit
    }
}


/**
 * 防止双击的网络请求
 */
inline fun <reified T> Call<T>.enqueue(
    view: View,
    noinline onSuccess: ((result: T) -> Unit),
    noinline onFinally: (() -> Unit)? = null,
    noinline onFail: ((t: Throwable?) -> Unit)? = null,
)
{
    view.isClickable = false
    view.isEnabled = false
    this.enqueue(object : SCallBack<T>()
    {
        override fun onSuccess(result: T)
        {
            onSuccess.invoke(result)
        }

        override fun onFinally()
        {
            onFinally?.invoke()
            view.isClickable = true
            view.isEnabled = true
        }

        override fun onFail(call: Call<T>, t: Throwable)
        {
            onFail?.invoke(t)
            onFail ?: let {
                super.onFail(call, t)
            }
        }
    })
}